﻿using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace MyLib
{
    public class MyAPI
    {
        public void SendMessage(string endPoint, Dictionary<string, string> parameters)
        {
            var populatedEndPoint = CreateFormattedPostRequest(parameters);
            byte[] bytes = Encoding.UTF8.GetBytes(populatedEndPoint);

            HttpWebRequest request = CreateWebRequest(endPoint, bytes.Length);

            using (var requestStream = request.GetRequestStream())
            {
                requestStream.Write(bytes, 0, bytes.Length);
            }

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    string message = String.Format("POST failed. Received HTTP {0}", response.StatusCode);
                    throw new ApplicationException(message);
                }
            }
        }

        private HttpWebRequest CreateWebRequest(string endPoint, Int32 contentLength)
        {
            var request = (HttpWebRequest)WebRequest.Create(endPoint);

            request.Method = "POST";
            request.ContentLength = contentLength;
            request.ContentType = "application/x-www-form-urlencoded";

            return request;
        }

        private string CreateFormattedPostRequest(Dictionary<string, string> values)
        {
            var paramterBuilder = new StringBuilder();
            var counter = 0;
            foreach (var value in values)
            {
                paramterBuilder.AppendFormat("{0}={1}", value.Key, HttpUtility.UrlEncode(value.Value));

                if (counter != values.Count - 1)
                {
                    paramterBuilder.Append("&");
                }

                counter++;
            }

            return paramterBuilder.ToString();

        }

        public static string TestPutBlob()
        {
            string sReturn = "OK";
            string endPoint = MyLib.MyAPI.MyStorageAccount.BlobEndpoint.ToString() + @"test/myblob";
            var request = (HttpWebRequest)WebRequest.Create(endPoint);

            request.Method = "PUT";
            request.ContentLength = 0; // Encoding.UTF8.GetBytes(endPoint).Length;

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    string message = String.Format("Filed. Received HTTP {0}", response.StatusCode);
                    //throw new ApplicationException(message);
                    sReturn = message;
                }
            }

            return sReturn;

        }

        //List Blobs msdn.microsoft.com/de-de/library/dd135734.aspx
        public static string TestGetBlobList()
        {
            string sReturn = "OK";
            var storageAccount = MyLib.MyAPI.MyStorageAccount;
            string endPoint = storageAccount.BlobEndpoint.ToString() + @"test?restype=container&comp=list";
            var request = (HttpWebRequest)WebRequest.Create(endPoint);

            request.Method = "Get";
            request.ContentLength = 0; // Encoding.UTF8.GetBytes(endPoint).Length;

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    string message = String.Format("Filed. Received HTTP {0}", response.StatusCode);
                    //throw new ApplicationException(message);
                    sReturn = message;
                }
                else
                {
                    StreamReader reader = new StreamReader(response.GetResponseStream());
                    sReturn = reader.ReadToEnd();
                }
            }

            return sReturn;

        }


        public static CloudStorageAccount MyStorageAccount
        {
            get
            {
                //TODO recheck, consider to use ctx

                var sa = clsStorageAccount.GetStorageAccount();
                var storageCredentialsAccountAndKey = new StorageCredentialsAccountAndKey(sa.StorageName, sa.StorageKey);
                CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(storageCredentialsAccountAndKey, true);

                return cloudStorageAccount;
            }
        }

        private static CloudBlobClient _myBlobClient = null;
        public static CloudBlobClient MyBlobClient
        {
            get
            {
                if (_myBlobClient == null)
                {
                    _myBlobClient = MyStorageAccount.CreateCloudBlobClient();
                }
                return _myBlobClient;
            }
        }

        public static CloudBlobContainer CreateBlobContainer(string containerName)
        {

            CloudBlobContainer container = MyBlobClient.GetContainerReference(containerName);
            container.CreateIfNotExist();
            return container;

        }

        public static string UploadBlockBlob(HttpPostedFile postedFile, string containerName, string blobName)
        {
            string myUrl = "";
            CloudBlobContainer container = CreateBlobContainer(containerName);
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.Properties.ContentType = postedFile.ContentType;
            blob.UploadFromStream(postedFile.InputStream);
            System.Diagnostics.Trace.TraceInformation("Uploaded");
            myUrl = blob.Uri.AbsoluteUri;
            return myUrl;
        }

        public static List<string> ContainerList(clsContext ctx)
        {
            List<string> myList = new List<string>();
            var blobClient = ctx.MyCloudBlobClient;
            foreach (CloudBlobContainer obj in blobClient.ListContainers())
            {
                myList.Add(obj.Name);
            }
            return myList;
        }

        public static List<string> BlobList(string ContainerName, out int i)
        {
            List<string> myList = new List<string>();
            var blobContainer = CreateBlobContainer(ContainerName);
            i = 0;
            GetBlobsFromDirectory(myList, blobContainer.ListBlobs());
            i = myList.Count;
            return myList;
        }

        private static void GetBlobsFromDirectory(List<string> myList, IEnumerable<IListBlobItem> listBlobs)
        {
            if (listBlobs == null)
            {
                return;
            }
            foreach (var obj in listBlobs)
            {
                CloudBlobDirectory blobDir = obj as CloudBlobDirectory;
                if (blobDir != null)
                {
                    GetBlobsFromDirectory(myList, blobDir.ListBlobs());
                    continue;
                }

                CloudBlob blob = obj as CloudBlob;
                if (blob != null)
                {
                    myList.Add(obj.Uri.AbsoluteUri);
                }
            }
        }

        public static int DeleteAllBlobs(string ContainerName)
        {
            List<string> myList = new List<string>();
            var blobContainer = CreateBlobContainer(ContainerName);

            int items = 0;

            DeleteAllBlobsFromDirectory(blobContainer.ListBlobs());

            return items;
        }

        private static void DeleteAllBlobsFromDirectory(IEnumerable<IListBlobItem> listBlobs)
        {
            if (listBlobs == null)
            {
                return;
            }
            foreach (var obj in listBlobs)
            {
                CloudBlobDirectory blobDir = obj as CloudBlobDirectory;
                if (blobDir != null)
                {
                    DeleteAllBlobsFromDirectory(blobDir.ListBlobs());
                    continue;
                }

                CloudBlob blob = obj as CloudBlob;
                if (blob != null)
                {
                    blob.Delete();
                }
            }
        }

        #region tables
        public static List<string> TableList
        {
            get
            {
                List<string> myList = new List<string>();
                var tableClient = MyStorageAccount.CreateCloudTableClient();
                foreach (string obj in tableClient.ListTables())
                {
                    myList.Add(obj);
                }
                return myList;
            }
        }

        public static List<string> Records(string TableName, int iAmount)
        {
            List<string> myList = new List<string>();
            CloudTableClient tableClient = MyStorageAccount.CreateCloudTableClient();
            TableServiceContext context = tableClient.GetDataServiceContext();
            context.ReadingEntity += new EventHandler<ReadingWritingEntityEventArgs>(OnReadingEntity);

            CloudTableQuery<GenericTableEntity> cloudTableQuery = null;
            cloudTableQuery =
                          (from entity in context.CreateQuery<GenericTableEntity>(TableName)
                           select entity).Take(iAmount).AsTableServiceQuery<GenericTableEntity>();

            IEnumerable<GenericTableEntity> entities = cloudTableQuery.Execute() as IEnumerable<GenericTableEntity>;
            string sItem = "";
            int records = 0;
            foreach (GenericTableEntity entity in entities)
            {

                // read each property, type and value in the payload   
                //var properties = entity.GetType().GetProperties();
                //foreach (var property in properties)
                //{
                //  var obj = property;
                //  entity.Properties.Add(property.Name, "test");
                //}

                sItem = entity.ToString();
                myList.Add(sItem);
                records++;
            }


            return myList;
        }

        private static void OnReadingEntity(object sender, ReadingWritingEntityEventArgs args)
        {
            XNamespace AtomNamespace = "http://www.w3.org/2005/Atom";
            XNamespace AstoriaDataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
            XNamespace AstoriaMetadataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";

            GenericTableEntity entity = args.Entity as GenericTableEntity;
            if (entity == null)
            {
                return;
            }

            // read each property, type and value in the payload   
            var properties = args.Entity.GetType().GetProperties();
            var q = from p in args.Data.Element(AtomNamespace + "content")
                                    .Element(AstoriaMetadataNamespace + "properties")
                                    .Elements()
                    where properties.All(pp => pp.Name != p.Name.LocalName)
                    select new
                    {
                        Name = p.Name.LocalName,
                        IsNull = string.Equals("true", p.Attribute(AstoriaMetadataNamespace + "null") == null ? null : p.Attribute(AstoriaMetadataNamespace + "null").Value, StringComparison.OrdinalIgnoreCase),
                        TypeName = p.Attribute(AstoriaMetadataNamespace + "type") == null ? null : p.Attribute(AstoriaMetadataNamespace + "type").Value,
                        p.Value
                    };

            foreach (var dp in q)
            {
                entity[dp.Name] = GetTypedEdmValue(dp.TypeName, dp.Value, dp.IsNull);
            }
        }


        private static object GetTypedEdmValue(string type, string value, bool isnull)
        {
            if (isnull) return null;

            if (string.IsNullOrEmpty(type)) return value;

            switch (type)
            {
                case "Edm.String": return value;
                case "Edm.Byte": return Convert.ChangeType(value, typeof(byte));
                case "Edm.SByte": return Convert.ChangeType(value, typeof(sbyte));
                case "Edm.Int16": return Convert.ChangeType(value, typeof(short));
                case "Edm.Int32": return Convert.ChangeType(value, typeof(int));
                case "Edm.Int64": return Convert.ChangeType(value, typeof(long));
                case "Edm.Double": return Convert.ChangeType(value, typeof(double));
                case "Edm.Single": return Convert.ChangeType(value, typeof(float));
                case "Edm.Boolean": return Convert.ChangeType(value, typeof(bool));
                case "Edm.Decimal": return Convert.ChangeType(value, typeof(decimal));
                case "Edm.DateTime": return XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.RoundtripKind);
                case "Edm.Binary": return Convert.FromBase64String(value);
                case "Edm.Guid": return new Guid(value);

                default: throw new NotSupportedException("Not supported type " + type);
            }
        }

        #endregion

        public static int DeleteAllRecordsFromTable(string tableName)
        {
            var tableClient = MyStorageAccount.CreateCloudTableClient();

            TableServiceContext context = tableClient.GetDataServiceContext();

            CloudTableQuery<GenericTableEntity> cloudTableQuery = null;
            cloudTableQuery =
                          (from entity in context.CreateQuery<GenericTableEntity>(tableName)
                           select entity).AsTableServiceQuery<GenericTableEntity>();

            IEnumerable<GenericTableEntity> entities = cloudTableQuery.Execute() as IEnumerable<GenericTableEntity>;

            int records = 0;
            foreach (GenericTableEntity entity in entities)
            {
                context.DeleteObject(entity);
                context.SaveChanges();
                records++;
            }

            return records;

        }

        public static string GetErrorMessage(Exception ex)
        {
            string s = ex.Message;
            if (ex.InnerException != null)
            {
                s += "\r\n" + GetErrorMessage(ex.InnerException);
            }
            return s;
        }
    }
}

